﻿using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using EnvDTE;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.IO;
using System.Diagnostics;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Drawing;
using System.Drawing.Design;
using System.Text;

namespace Adaptive.Foundation.CodeDom.Native
{
    public class VsCodeDomParser : CodeParser
    {
        // Fields
        private EventHandler ehFunctionPointHandler;
        private string fileName;
        private bool implementsBodyPoint = true;
        private VsCodeDomProvider provider;
        internal static string VbHandlesClausesKey = "_vbHandlesClauses";
        internal const int vsCMWithEvents = 0x80;
        internal static Type VsElementKey = typeof(CodeElement);
        internal static string VsGenerateStatementsKey = "_vsGenerateStatements";
        internal static string VsOriginalCollectionKey = "_vsOriginalCollection";
        private CodeDomXmlProcessor xmlProcessor;

        // Methods
        public VsCodeDomParser(VsCodeDomProvider provider)
        {
            this.provider = provider;
            this.ehFunctionPointHandler = new EventHandler(this.GetFunctionPoints);
        }

        private CodeTypeDeclaration CodeTypeDeclarationFromCodeClass(CodeClass vsClass)
        {
            CodeTypeDeclaration codeObject = new CodeTypeDeclaration(vsClass.Name);
            codeObject.LinePragma = new CodeLinePragma(this.FileName, vsClass.StartPoint.Line);
            this.SetVsElementToCodeObject((CodeElement)vsClass, codeObject);
            codeObject.PopulateMembers += new EventHandler(this.OnTypePopulateMembers);
            if (this.provider.IsVB)
            {
                //codeObject.UserData[typeof(System.Windows.Forms.Design.EventsTab)] = "Hide";
            }
            codeObject.Attributes = this.GetMemberAttributesFromVsClass(vsClass);
            codeObject.TypeAttributes = TypeAttributes.AutoLayout;
            try
            {
                foreach (CodeElement element in vsClass.Bases)
                {
                    if ((int)element.Kind == 1)
                    {
                        codeObject.BaseTypes.Add(new CodeTypeReference(this.GetUrtTypeFromVsType((CodeClass)element)));
                    }
                }
            }
            catch (Exception exception)
            {
                if (!this.provider.CodeGenerator.IsValidIdentifier(vsClass.Name))
                {
                    throw new Exception("InvalidClassNameIdentifier");
                }
                throw exception;
            }

            return codeObject;
        }

        private CodeAttachEventStatement CreateEventAttachStatement(string fieldName, EventDescriptor eventDesc, string handlerMethod)
        {
            CodeExpression targetObject = new CodeThisReferenceExpression();
            CodeExpression expression2 = (fieldName == null) ? targetObject : new CodeFieldReferenceExpression(targetObject, fieldName);
            CodeExpression listener = new CodeObjectCreateExpression(eventDesc.EventType.FullName, new CodeExpression[] { new CodeMethodReferenceExpression(targetObject, handlerMethod) });
            CodeAttachEventStatement statement = new CodeAttachEventStatement(expression2, eventDesc.Name, listener);
            statement.UserData["GeneratedStatement"] = true;
            return statement;
        }

        private void GetDelegateHookupsFromHandlesClauses(Hashtable handlers, CodeTypeDeclaration codeTypeDecl, CodeStatementCollection statements)
        {
            Hashtable hashtable = new Hashtable();
            foreach (CodeTypeMember member in codeTypeDecl.Members)
            {
                CodeMemberField field = member as CodeMemberField;
                if (field != null)
                {
                    hashtable[field.Name] = field.Type.BaseType;
                }
            }
            hashtable["MyBase"] = codeTypeDecl.BaseTypes[0].BaseType;
            hashtable[codeTypeDecl.Name] = codeTypeDecl.BaseTypes[0].BaseType;
            ITypeResolutionService typeLoader = this.provider.TypeLoader;
            Type type = null;
            if (typeLoader != null)
            {
                foreach (DictionaryEntry entry in handlers)
                {
                    string key = (string)entry.Key;
                    int index = key.IndexOf('.');
                    string str2 = key.Substring(0, index);
                    object obj2 = hashtable[str2];
                    Type componentType = null;
                    if (obj2 == null)
                    {
                        if (type == null)
                        {
                            type = typeLoader.GetType(codeTypeDecl.BaseTypes[0].BaseType, false);
                        }
                        if (type != null)
                        {
                            foreach (FieldInfo info in type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance))
                            {
                                if (info.Name == str2)
                                {
                                    componentType = info.FieldType;
                                    break;
                                }
                                object[] customAttributes = info.GetCustomAttributes(typeof(AccessedThroughPropertyAttribute), false);
                                if ((customAttributes != null) && (customAttributes.Length > 0))
                                {
                                    AccessedThroughPropertyAttribute attribute = (AccessedThroughPropertyAttribute)customAttributes[0];
                                    if (attribute.PropertyName == str2)
                                    {
                                        PropertyInfo property = type.GetProperty(attribute.PropertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
                                        if (property != null)
                                        {
                                            MethodInfo getMethod = property.GetGetMethod(true);
                                            if ((getMethod != null) && !getMethod.IsPrivate)
                                            {
                                                componentType = property.PropertyType;
                                            }
                                            break;
                                        }
                                    }
                                }
                            }
                            if (componentType != null)
                            {
                                hashtable[str2] = componentType;
                                obj2 = componentType;
                            }
                            else
                            {
                                hashtable[str2] = false;
                                obj2 = false;
                                continue;
                            }
                        }
                    }
                    if (obj2 is string)
                    {
                        Type type3 = typeLoader.GetType((string)obj2, false);
                        if (type3 != null)
                        {
                            hashtable[str2] = type3;
                            componentType = type3;
                        }
                    }
                    else if (obj2 is Type)
                    {
                        componentType = (Type)obj2;
                    }
                    else
                    {
                        if (obj2 is bool)
                        {
                        }
                        continue;
                    }
                    if (componentType != null)
                    {
                        string name = key.Substring(index + 1);
                        EventDescriptor eventDesc = TypeDescriptor.GetEvents(componentType)[name];
                        if (eventDesc == null)
                        {
                            foreach (Type type4 in componentType.GetInterfaces())
                            {
                                EventInfo info4 = type4.GetEvent(name);
                                if (info4 != null)
                                {
                                    eventDesc = TypeDescriptor.CreateEvent(type4, name, info4.EventHandlerType, new Attribute[0]);
                                    break;
                                }
                            }
                        }
                        if (eventDesc != null)
                        {
                            CodeMemberMethod method = entry.Value as CodeMemberMethod;
                            CodeStatement statement = this.CreateEventAttachStatement((str2 == "MyBase") ? null : str2, eventDesc, method.Name);
                            statement.UserData["_XmlCodeDomParseStmt"] = "_XmlCodeDomParseStmt";
                            statements.Add(statement);
                        }
                    }
                }
            }
        }

        private void GetFunctionPoints(object sender, EventArgs e)
        {
            CodeMemberMethod method = sender as CodeMemberMethod;
            if (method != null)
            {
                object obj2;
                CodeFunction function = method.UserData[VsElementKey] as CodeFunction;
                TextPoint startPoint = null;
                try
                {
                    if (this.implementsBodyPoint)
                    {
                        startPoint = function.GetStartPoint((EnvDTE.vsCMPart)0x20);
                    }
                }
                catch
                {
                    this.implementsBodyPoint = false;
                }
                if (((startPoint == null) && (function is IMethodXML)) && (((IMethodXML)function).GetBodyPoint(out obj2) == 0))
                {
                    startPoint = obj2 as TextPoint;
                }
                if (startPoint != null)
                {
                    Point point2 = new Point(startPoint.LineCharOffset, startPoint.Line);
                    method.UserData[typeof(Point)] = point2;
                }
                method.LinePragma = new CodeLinePragma(this.FileName, function.StartPoint.Line);
            }
        }

        private string[] GetHandlesClauses(IEventHandler eh)
        {
            IVsEnumBstr handledEvents = eh.GetHandledEvents();
            if (handledEvents == null)
            {
                return new string[0];
            }
            string pbstr = null;
            int pceltFetched = 0;
            string[] strArray = new string[handledEvents.GetCount()];
            int num2 = 0;
            while ((handledEvents.Next(1, out pbstr, out pceltFetched) == 0) && (pceltFetched == 1))
            {
                strArray[num2++] = pbstr;
            }
            return strArray;
        }

        private MemberAttributes GetMemberAttributesFromVsAccess(vsCMAccess vsAccess)
        {
            if (this.provider.IsVB)
            {
                vsAccess &= ((EnvDTE.vsCMAccess) (-129));
            }
            switch (vsAccess)
            {
                case (EnvDTE.vsCMAccess)1:
                    return MemberAttributes.Public;

                case (EnvDTE.vsCMAccess)2:
                    return MemberAttributes.Private;

                case (EnvDTE.vsCMAccess)4:
                    return MemberAttributes.Assembly;

                case (EnvDTE.vsCMAccess)8:
                    return MemberAttributes.Family;

                case (EnvDTE.vsCMAccess)12:
                    return MemberAttributes.FamilyOrAssembly;
            }
            return MemberAttributes.Private;
        }

        private MemberAttributes GetMemberAttributesFromVsClass(CodeClass vsClass)
        {
            MemberAttributes memberAttributesFromVsAccess = this.GetMemberAttributesFromVsAccess(vsClass.Access);
            if (vsClass.IsAbstract)
            {
                memberAttributesFromVsAccess |= MemberAttributes.Abstract;
            }
            return memberAttributesFromVsAccess;
        }

        private MemberAttributes GetMemberAttributesFromVsField(CodeVariable vsField)
        {
            MemberAttributes memberAttributesFromVsAccess = this.GetMemberAttributesFromVsAccess(vsField.Access);
            if (vsField.IsShared)
            {
                memberAttributesFromVsAccess |= MemberAttributes.Static;
            }
            if (vsField.IsConstant)
            {
                memberAttributesFromVsAccess |= MemberAttributes.Const;
            }
            return memberAttributesFromVsAccess;
        }

        private MemberAttributes GetMemberAttributesFromVsFunction(CodeFunction vsFunction)
        {
            MemberAttributes memberAttributesFromVsAccess = this.GetMemberAttributesFromVsAccess(vsFunction.Access);
            if (vsFunction.IsShared)
            {
                memberAttributesFromVsAccess |= MemberAttributes.Static;
            }
            if (vsFunction.MustImplement)
            {
                memberAttributesFromVsAccess |= MemberAttributes.Abstract;
            }
            if (!vsFunction.CanOverride && ((memberAttributesFromVsAccess & MemberAttributes.Private) == ((MemberAttributes)0)))
            {
                memberAttributesFromVsAccess |= MemberAttributes.Final;
            }
            return memberAttributesFromVsAccess;
        }

        private string GetUrtTypeFromVsType(CodeClass vsClass)
        {
            string str = vsClass.FullName;
            if ((int)vsClass.InfoLocation != 4)
            {
                while ((vsClass != null) && (vsClass.Parent is CodeClass))
                {
                    int length = str.LastIndexOf('.');
                    if (length != -1)
                    {
                        str = str.Substring(0, length) + '+' + str.Substring(length + 1);
                    }
                    vsClass = (CodeClass)vsClass.Parent;
                }
            }
            return str;
        }

        private string GetUrtTypeFromVsType(CodeTypeRef vsType)
        {
            string str = "";
            if ((int)vsType.TypeKind == 2)
            {
                CodeTypeRef ref2 = vsType;
                while ((int)ref2.TypeKind == 2)
                {
                    ref2 = ref2.ElementType;
                }
                str = ref2.AsFullName;
            }
            else
            {
                str = vsType.AsFullName;
            }
            if ((int)vsType.TypeKind == 1)
            {
                for (CodeType type = vsType.CodeType; ((type != null) && ((int)type.InfoLocation != 4)) && (type.Parent is CodeType); type = (CodeType)type.Parent)
                {
                    int length = str.LastIndexOf('.');
                    if (length != -1)
                    {
                        str = str.Substring(0, length) + '+' + str.Substring(length + 1);
                    }
                }
            }
            return str;
        }

        private CodeElement GetVsElementFromCodeObject(CodeObject codeObject)
        {
            return (CodeElement)codeObject.UserData[VsElementKey];
        }

        private void OnMethodPopulateParameters(object sender, EventArgs e)
        {
            CodeMemberMethod codeObject = (CodeMemberMethod)sender;
            CodeFunction vsElementFromCodeObject = (CodeFunction)this.GetVsElementFromCodeObject(codeObject);
            try
            {
                ITypeResolutionService typeLoader = this.provider.TypeLoader;
                foreach (CodeElement element in vsElementFromCodeObject.Parameters)
                {
                    if ((int)element.Kind != 6)
                    {
                        continue;
                    }
                    CodeParameter parameter = (CodeParameter)element;
                    FieldDirection @in = FieldDirection.In;
                    StringBuilder builder = new StringBuilder(this.GetUrtTypeFromVsType(parameter.Type));
                    Type type = typeLoader.GetType(builder.ToString());
                    bool flag = false;
                    if (parameter is IParameterKind)
                    {
                        IParameterKind kind = (IParameterKind)parameter;
                        switch (kind.GetParameterPassingMode())
                        {
                            case 2:
                                @in = FieldDirection.Out;
                                break;

                            case 3:
                                @in = FieldDirection.Ref;
                                break;
                        }
                        if (type != null)
                        {
                            for (int i = kind.GetParameterArrayCount() - 1; i >= 0; i--)
                            {
                                flag = true;
                                builder.Append("[");
                                for (int j = 1; j < kind.GetParameterArrayDimensions(i); j++)
                                {
                                    builder.Append(",");
                                }
                                builder.Append("]");
                            }
                        }
                    }
                    if ((type != null) && flag)
                    {
                        type = type.Assembly.GetType(builder.ToString());
                    }
                    CodeParameterDeclarationExpression expression = new CodeParameterDeclarationExpression(new CodeTypeReference(type), parameter.Name);
                    expression.Direction = @in;
                    codeObject.Parameters.Add(expression);
                }
            }
            catch
            {
            }
        }

        private void OnMethodPopulateStatements(object sender, EventArgs e)
        {
            CodeMemberMethod codeObject = (CodeMemberMethod)sender;
            CodeFunction vsElementFromCodeObject = (CodeFunction)this.GetVsElementFromCodeObject(codeObject);
            IMethodXML dxml = null;
            try
            {
                dxml = (IMethodXML)vsElementFromCodeObject;
            }
            catch
            {
            }
            if (dxml != null)
            {
                string pbstrXML = null;
                try
                {
                    dxml.GetXML(ref pbstrXML);
                }
                catch (Exception)
                {
                    throw new Exception("FailedToParseMethod");
                }
                this.XmlProcessor.ParseXml(pbstrXML, codeObject.Statements, this.FileName, codeObject.Name);
                codeObject.UserData[VsGenerateStatementsKey] = VsGenerateStatementsKey;
                this.provider.ClearGeneratedMembers();
                if (this.provider.IsVB)
                {
                    CodeTypeDeclaration codeTypeDecl = (CodeTypeDeclaration)codeObject.UserData[typeof(CodeTypeDeclaration)];
                    if (codeTypeDecl != null)
                    {
                        Hashtable handlers = (Hashtable)codeTypeDecl.UserData[VbHandlesClausesKey];
                        this.provider.FullParseHandlers = handlers;
                        if ((handlers != null) && (handlers.Count != 0))
                        {
                            this.GetDelegateHookupsFromHandlesClauses(handlers, codeTypeDecl, codeObject.Statements);
                        }
                    }
                }
            }
        }

        private void OnNamespacePopulateTypes(object sender, EventArgs e)
        {
            System.CodeDom.CodeNamespace codeObject = (System.CodeDom.CodeNamespace)sender;
            EnvDTE.CodeNamespace vsElementFromCodeObject = (EnvDTE.CodeNamespace)this.GetVsElementFromCodeObject(codeObject);
            EventHandler handler = new EventHandler(this.OnNamespacePopulateTypes);
            foreach (CodeElement element in vsElementFromCodeObject.Members) 
            {
                if ((int)element.Kind == 1)
                {
                    CodeTypeDeclaration declaration = this.CodeTypeDeclarationFromCodeClass((CodeClass)element);
                    codeObject.Types.Add(declaration);
                    continue;
                }
                if ((int)element.Kind == 5)
                {
                    System.CodeDom.CodeNamespace namespace4 = new System.CodeDom.CodeNamespace(((EnvDTE.CodeNamespace)element).FullName);
                    this.SetVsElementToCodeObject(element, namespace4);
                    namespace4.PopulateTypes += handler;
                    CodeCompileUnit unit = (CodeCompileUnit)codeObject.UserData[typeof(CodeCompileUnit)];
                    if (unit != null)
                    {
                        int index = unit.Namespaces.IndexOf(codeObject);
                        if (index != -1)
                        {
                            unit.Namespaces[index] = namespace4;
                            namespace4.UserData[typeof(CodeCompileUnit)] = unit;
                        }
                    }
                    CodeTypeDeclarationCollection types = namespace4.Types;
                }
            }
        }

        private void OnTypePopulateMembers(object sender, EventArgs e)
        {
            CodeTypeDeclaration codeObject = (CodeTypeDeclaration)sender;
            CodeClass vsElementFromCodeObject = (CodeClass)this.GetVsElementFromCodeObject(codeObject);
            CodeTypeMemberCollection members = new CodeTypeMemberCollection();
            codeObject.UserData[VsOriginalCollectionKey] = members;
            string name = codeObject.Name;
            int num = name.LastIndexOf('.');
            if (num != -1)
            {
                name = name.Substring(num + 1);
            }
            foreach (CodeElement element in vsElementFromCodeObject.Members)
            {
                CodeFunction function;
                CodeMemberMethod method;
                switch (element.Kind)
                {
                    case (vsCMElement)2:
                        function = (CodeFunction)element;
                        if (!(function.Name == name))
                        {
                            break;
                        }
                        method = new CodeTypeConstructor();
                        goto Label_016F;

                    case (vsCMElement)3:
                        {
                            CodeVariable vsField = (CodeVariable)element;
                            CodeMemberField field = new CodeMemberField(new CodeTypeReference(this.GetUrtTypeFromVsType(vsField.Type)), vsField.Name);
                            field.LinePragma = new CodeLinePragma(this.FileName, vsField.StartPoint.Line);
                            field.Attributes = this.GetMemberAttributesFromVsField(vsField);
                            this.SetVsElementToCodeObject((CodeElement)vsField, field);
                            codeObject.Members.Add(field);
                            members.Add(field);
                            continue;
                        }
                    default:
                        {
                            continue;
                        }
                }
                method = new CodeMemberMethod();
                string urtTypeFromVsType = this.GetUrtTypeFromVsType(function.Type);
                if ((urtTypeFromVsType == null) || (urtTypeFromVsType.Length == 0))
                {
                    urtTypeFromVsType = typeof(void).FullName;
                }
                method.ReturnType = new CodeTypeReference(urtTypeFromVsType);
            Label_016F:
                method.Name = function.Name;
                method.UserData[typeof(EventHandler)] = this.ehFunctionPointHandler;
                this.SetVsElementToCodeObject((CodeElement)function, method);
                method.Attributes = this.GetMemberAttributesFromVsFunction(function);
                method.PopulateParameters += new EventHandler(this.OnMethodPopulateParameters);
                method.PopulateStatements += new EventHandler(this.OnMethodPopulateStatements);
                codeObject.Members.Add(method);
                members.Add(method);
                method.UserData[typeof(CodeTypeDeclaration)] = codeObject;
                if (this.provider.IsVB && (element is IEventHandler))
                {
                    string[] handlesClauses = this.GetHandlesClauses((IEventHandler)element);
                    Hashtable hashtable = (Hashtable)codeObject.UserData[VbHandlesClausesKey];
                    foreach (string str3 in handlesClauses)
                    {
                        if (str3 != null)
                        {
                            if (hashtable == null)
                            {
                                hashtable = new Hashtable();
                                codeObject.UserData[VbHandlesClausesKey] = hashtable;
                            }
                            hashtable[str3] = method;
                        }
                    }
                }
            }
        }

        public override CodeCompileUnit Parse(TextReader codeStream)
        {
            this.fileName = null;
            CodeCompileUnit unit = new CodeCompileUnit();
            System.CodeDom.CodeNamespace defaultNamespace = null;

            if (provider.FileCodeModel != null)
            {
                foreach (CodeElement element in this.provider.FileCodeModel.CodeElements)
                {
                    System.CodeDom.CodeNamespace codeObject = null;

                    if ((int)element.Kind == 5)
                    {
                        codeObject = new System.CodeDom.CodeNamespace(((EnvDTE.CodeNamespace)element).FullName);
                        this.SetVsElementToCodeObject(element, codeObject);
                        codeObject.PopulateTypes += new EventHandler(this.OnNamespacePopulateTypes);
                    }
                    else if ((int)element.Kind == 1)
                    {
                        bool flag = false;
                        if (defaultNamespace == null)
                        {
                            defaultNamespace = this.provider.DefaultNamespace;
                        }
                        else
                        {
                            flag = true;
                        }
                        codeObject = defaultNamespace;
                        CodeTypeDeclaration declaration = this.CodeTypeDeclarationFromCodeClass((CodeClass)element);
                        codeObject.Types.Add(declaration);
                        if (flag)
                        {
                            continue;
                        }
                    }
                    if (codeObject != null)
                    {
                        unit.Namespaces.Add(codeObject);
                        codeObject.UserData[typeof(CodeCompileUnit)] = unit;
                        CodeTypeDeclarationCollection types = codeObject.Types;
                    }
                }
            }
            if (codeStream is IServiceProvider)
            {
                this.provider.EnsureExtender((IServiceProvider)codeStream);
            }
            return unit;
        }

        private void SetVsElementToCodeObject(CodeElement vsElement, CodeObject codeObject)
        {
            codeObject.UserData[VsElementKey] = vsElement;
        }
        
        // Properties
        private string FileName
        {
            get
            {
                if (this.fileName == null)
                {
                    this.fileName = this.provider.FileName;
                }
                return this.fileName;
            }
        }

        public bool SupportsStatementGeneration
        {
            get
            {
                return this.provider.IsVB;
            }
        }

        private CodeDomXmlProcessor XmlProcessor
        {
            get
            {
                if (this.xmlProcessor == null)
                {
                    this.xmlProcessor = new CodeDomXmlProcessor();
                }
                return this.xmlProcessor;
            }
        }
    }
}